home *** CD-ROM | disk | FTP | other *** search
/ Ian & Stuart's Australian Mac 1993 September / September 93.iso / Archives / Games / Strategy / Puzzle / GameMaster / GM Dev Kit / Rulebook Sources / TicTacToe / TicTacToe.p next >
Encoding:
Text File  |  1991-12-05  |  9.4 KB  |  386 lines  |  [TEXT/PJMM]

  1. unit TicTacToe;
  2.  
  3. { ©1991 Quinn "The Eskimo" }
  4.  
  5. interface
  6.  
  7.     uses
  8.         QuickDrawRules, {}
  9.         GameTypes, {}
  10.         NumSubs, Failure, Debugs, DialogSubs;
  11.  
  12.     procedure Main (var gameevent: gameEventRecord);
  13.  
  14. implementation
  15.  
  16.     const
  17.         rIconBase = 1000;
  18.         rStrokeBase = 1100;
  19.         rMainDialog = 1000;
  20.         dit_stroke = 15;
  21.  
  22.     type
  23.         xNdx = 1..3;
  24.         yNdx = 1..3;
  25.  
  26.         cellType = (Cempty, Coh, Cex);
  27.         playerType = (Poh, Pex);
  28.         playStateType = (ps_Playing, ps_GameOver);
  29.         connectionStateType = (cs_Local, cs_Remote);
  30.         strokeStateType = (horiz, vert, leftdiag, rightdiag);
  31.         boardType = array[xNdx, yNdx] of cellType;
  32.  
  33.         globalsRecord = record
  34.                 icons: array[cellType] of handle;
  35.                 strokes: array[strokeStateType] of PicHandle;
  36.             end;
  37.         globalsPtr = ^globalsRecord;
  38.         globalsPeek = ^globalsPtr;
  39.  
  40.  
  41.         gameRecord = record
  42.                 globals: globalsPeek;
  43.                 connectionstate: connectionStateType;
  44.                 playertomove: playerType;
  45.                 localplayer: playerType;
  46.                 playstate: playStateType;
  47.                 board: boardType;
  48.                 moved: set of 1..9;
  49.                 strokestate: strokeStateType;
  50.                 strokendx: integer;
  51.             end;
  52.         gamePtr = ^gameRecord;
  53.         gamePeek = ^gamePtr;
  54.  
  55.     procedure StrokeUserItem (dlg: DialogPtr; itemNo: integer);
  56.         var
  57.             r, myrect: rect;
  58.             pic: PicHandle;
  59.             gp: gamePeek;
  60.     begin
  61.         gp := gamePeek(WindowPeek(dlg)^.refcon);
  62.         if gp^^.playstate = ps_GameOver then begin
  63.             GetDRect(dlg, itemNo, r);
  64.             pic := gp^^.globals^^.strokes[gp^^.strokestate];
  65.             myrect := pic^^.picFrame;
  66.             OffsetRect(myrect, -myrect.left, -myrect.top);
  67.             OffsetRect(myrect, r.left, r.top);
  68.             case gp^^.strokestate of
  69.                 horiz: 
  70.                     OffsetRect(myrect, 0, (gp^^.strokendx - 1) * 33 - 3);
  71.                 vert: 
  72.                     OffsetRect(myrect, (gp^^.strokendx - 1) * 33, 0);
  73.                 otherwise
  74.             end; { case }
  75.             DrawPicture(pic, myrect);
  76.         end; { if }
  77.     end; { StrokeUserItem }
  78.  
  79.     procedure Main (var gameevent: gameEventRecord);
  80.  
  81.         procedure InitRuleBook;
  82.             var
  83.                 gp: globalsPeek;
  84.                 c: cellType;
  85.                 rh: DialogTHndl;
  86.                 s: strokeStateType;
  87.         begin
  88.             gameevent.globals := NewHandle(sizeof(globalsRecord));
  89.             HLock(gameevent.globals);
  90.             gp := globalsPeek(gameevent.globals);
  91.             for c := Cempty to Cex do begin
  92.                 gp^^.icons[c] := GetIcon(rIconBase + ord(c));
  93.                 FailNil(gp^^.icons[c], 'Icon not found');
  94.                 FailResError('Icon error');
  95.             end; { for }
  96.             for s := horiz to rightdiag do begin
  97.                 gp^^.strokes[s] := GetPicture(ord(s) + rStrokeBase);
  98.                 FailNil(gp^^.strokes[s], 'stroke missing');
  99.                 FailResError('stroke error');
  100.             end; { for }
  101.             rh := DialogTHndl(GetResource('DLOG', rMainDialog));
  102.             FailNil(rh, 'Dialog not found');
  103.             FailResError('Dialog error');
  104.             gameevent.int1 := rh^^.boundsRect.right - rh^^.boundsRect.left;
  105.             gameevent.int2 := rh^^.boundsRect.bottom - rh^^.boundsRect.left;
  106.             ReleaseResource(Handle(rh));
  107.             HUnlock(gameevent.globals);
  108.         end; { InitRuleBook }
  109.  
  110.         procedure FinishRuleBook;
  111.         begin
  112.             DisposeHandle(gameevent.globals);
  113.             gameevent.globals := nil;
  114. { Dont release resources as the resource file will be closed (hopefully) }
  115.         end; { FinishRuleBook }
  116.  
  117.         function Opposite (p: playerType): playerType;
  118.         begin
  119.             case p of
  120.                 Poh: 
  121.                     Opposite := Pex;
  122.                 Pex: 
  123.                     Opposite := Poh;
  124.             end; { case }
  125.         end; { Opposite }
  126.  
  127.         procedure PseudoMain (var game: gameRecord; var globals: globalsRecord);
  128.  
  129.             procedure PlacePiece (p: playerType; x: xNdx; y: yNdx);
  130.                 var
  131.                     c: cellType;
  132.                     r: Rect;
  133.                     dit: integer;
  134.             begin
  135.                 case p of
  136.                     Pex: 
  137.                         c := Cex;
  138.                     Poh: 
  139.                         c := Coh;
  140.                 end; { case }
  141.                 game.board[x, y] := c;
  142.                 dit := (x - 1) * 3 + y;
  143.                 SetDHandle(QDGlobals^.thePort, dit, globals.icons[c]);
  144.                 GetDRect(QDGlobals^.thePort, dit, r);
  145.                 InvalRect(r);
  146.                 game.moved := game.moved + [dit];
  147.             end; { PlacePiece }
  148.  
  149.             procedure MoveComplete;
  150.  
  151.                 function GameOver: boolean;
  152.                     var
  153.                         res: boolean;
  154.                     procedure CheckLine (x1, y1, x2, y2, x3, y3: integer; ss: strokeStateType; sn: integer);
  155.                     begin
  156.                         if not res and ((game.board[x1, y1] <> Cempty) and (game.board[x1, y1] = game.board[x2, y2]) and (game.board[x2, y2] = game.board[x3, y3])) then begin
  157.                             res := true;
  158.                             game.strokestate := ss;
  159.                             game.strokendx := sn;
  160.                         end; { if }
  161.                     end; { CheckLine }
  162.                 begin
  163.                     res := false;
  164.                     CheckLine(1, 1, 1, 2, 1, 3, vert, 1);
  165.                     CheckLine(2, 1, 2, 2, 2, 3, vert, 2);
  166.                     CheckLine(3, 1, 3, 2, 3, 3, vert, 3);
  167.  
  168.                     CheckLine(1, 1, 2, 1, 3, 1, horiz, 1);
  169.                     CheckLine(1, 2, 2, 2, 3, 2, horiz, 2);
  170.                     CheckLine(1, 3, 2, 3, 3, 3, horiz, 3);
  171.  
  172.                     CheckLine(1, 1, 2, 2, 3, 3, leftdiag, 0);
  173.                     CheckLine(3, 1, 2, 2, 1, 3, rightdiag, 0);
  174.                     GameOver := res;
  175.                 end; { GameOver }
  176.                 var
  177.                     r: Rect;
  178.             begin
  179.                 game.playertomove := Opposite(game.playertomove);
  180.                 if GameOver then begin
  181.                     game.playstate := ps_GameOver;
  182.                     GetDRect(QDGlobals^.thePort, dit_stroke, r);
  183.                     InvalRect(r);
  184.                 end; { if }
  185.             end; { MoveComplete }
  186.  
  187.             procedure UpdateStatus;
  188.             begin
  189.                 case game.connectionstate of
  190.                     cs_Local: 
  191.                         gameevent.myturn := true;
  192.                     cs_Remote: 
  193.                         gameevent.myturn := (game.playertomove = game.localplayer);
  194.                 end; { case }
  195.             end; { UpdateStatus }
  196.  
  197.             procedure CommonInit;
  198.             begin
  199.                 SetDHandle(QDGlobals^.thePort, dit_stroke, @StrokeUserItem);
  200.                 SetWRefCon(QDGlobals^.thePort, longint(gameevent.game));
  201.                 game.globals := globalsPeek(gameevent.globals);
  202.             end; { CommonInit }
  203.  
  204.             procedure ClearGameState;
  205.                 var
  206.                     x: xNdx;
  207.                     y: yNdx;
  208.             begin
  209.                 for x := 1 to 3 do begin
  210.                     for y := 1 to 3 do begin
  211.                         game.board[x, y] := Cempty;
  212.                     end; { for }
  213.                 end; { for }
  214.                 game.playstate := ps_Playing;
  215.                 game.playertomove := Pex;
  216.                 game.moved := [];
  217.             end; { ClearGameState }
  218.  
  219.             procedure ChangeHandles;
  220.                 var
  221.                     x: xNdx;
  222.                     y: yNdx;
  223.                     dit: integer;
  224.             begin
  225.                 for x := 1 to 3 do begin
  226.                     for y := 1 to 3 do begin
  227.                         dit := (x - 1) * 3 + y;
  228.                         SetDHandle(QDGlobals^.thePort, dit, globals.icons[game.board[x, y]]);
  229.                     end; { for }
  230.                 end; { for }
  231.             end; { ChangeHandles }
  232.  
  233.             procedure NewGame;
  234.             begin
  235.                 CommonInit;
  236.                 ClearGameState;
  237.                 ChangeHandles;
  238.                 game.localplayer := Pex;
  239.                 game.connectionstate := cs_Local;
  240.             end; { NewGame }
  241.  
  242.             procedure OldGame;
  243.             begin
  244.                 CommonInit;
  245.                 ChangeHandles;
  246.                 game.connectionstate := cs_Local;
  247.             end; { OldGame }
  248.  
  249.             procedure Swap;
  250.             begin
  251.                 game.localplayer := Opposite(game.localplayer);
  252.             end; { Swap }
  253.  
  254.             procedure Restart;
  255.             begin
  256.                 ClearGameState;
  257.                 ChangeHandles;
  258.                 InvalRect(QDGlobals^.thePort^.portRect);
  259.             end; { Restart }
  260.  
  261.             procedure ConnectionMade;
  262.             begin
  263.                 game.connectionstate := cs_Remote;
  264.                 UpdateStatus;
  265.             end; { ConnectionMade }
  266.  
  267.             procedure ConnectionLost;
  268.             begin
  269.                 game.connectionstate := cs_Local;
  270.                 UpdateStatus;
  271.             end; { ConnectionLost }
  272.  
  273.             procedure MessageReceived;
  274.                 var
  275.                     x: xNdx;
  276.                     y: yNdx;
  277.             begin
  278.                 if game.connectionstate <> cs_Remote then begin
  279.                     Failure('Message while not connected');
  280.                 end
  281.                 else if Length(gameevent.message) <> 2 then begin
  282.                     Failure('Message not the right length');
  283.                 end
  284.                 else begin
  285.                     x := ord(gameevent.message[1]) - ord('0');
  286.                     y := ord(gameevent.message[2]) - ord('0');
  287.                     PlacePiece(game.playertomove, x, y);
  288.                     MoveComplete;
  289.                     UpdateStatus;
  290.                 end;
  291.             end; { MessageReceived }
  292.  
  293.             procedure SendMove (x: xNdx; y: yNdx);
  294.             begin
  295.                 gameevent.message := concat(chr(x + ord('0')), chr(y + ord('0')));
  296.                 gameevent.event := ge_SendMessage;
  297.             end; { SendMove }
  298.  
  299.             procedure MouseDown;
  300.  
  301.                 function TrackMouse (x: xNdx; y: yNdx): boolean;
  302.                     var
  303.                         r: Rect;
  304.                         mouse: Point;
  305.                         inside, inverted: boolean;
  306.                 begin
  307.                     GetDRect(QDGlobals^.thePort, gameevent.int1, r);
  308.                     inverted := false;
  309.                     while StillDown do begin
  310.                         GetMouse(mouse);
  311.                         inside := PtInRect(mouse, r);
  312.                         if (inside and not inverted) or (inverted and not inside) then begin
  313.                             inverted := not inverted;
  314.                             InvertRect(r);
  315.                         end; { if }
  316.                     end; { while }
  317.                     if inverted then begin
  318.                         InvertRect(r);
  319.                     end; { if }
  320.                     TrackMouse := inside;
  321.                 end; { TrackMouse }
  322.  
  323.                 var
  324.                     x: xNdx;
  325.                     y: yNdx;
  326.             begin
  327.                 if not (gameevent.int1 in game.moved) and (game.playstate = ps_Playing) then begin
  328.                     x := (gameevent.int1 - 1) div 3 + 1;
  329.                     y := (gameevent.int1 - 1) mod 3 + 1;
  330.                     if TrackMouse(x, y) then begin
  331.                         PlacePiece(game.playertomove, x, y);
  332.                         if game.connectionstate = cs_Remote then begin
  333.                             SendMove(x, y);
  334.                         end; { if }
  335.                         MoveComplete;
  336.                         UpdateStatus;
  337.                     end; { if }
  338.                 end; { if }
  339.             end; { MouseDown }
  340.  
  341.         begin { PseudoMain }
  342.             case gameevent.event of
  343.                 ge_NewGame: 
  344.                     NewGame;
  345.                 ge_OldGame: 
  346.                     OldGame;
  347.                 ge_Swap: 
  348.                     Swap;
  349.                 ge_Restart: 
  350.                     Restart;
  351.                 ge_ConnectionLost: 
  352.                     ConnectionLost;
  353.                 ge_ConnectionMade: 
  354.                     ConnectionMade;
  355.                 ge_MessageReceived: 
  356.                     MessageReceived;
  357.                 ge_MouseDown: 
  358.                     MouseDown;
  359.                 otherwise
  360.             end; { case }
  361.         end; { PseudoMain }
  362.  
  363.         var
  364.             s1, s2: SignedByte;
  365.     begin { Main }
  366.         case gameevent.event of
  367.             ge_InitRuleBook: 
  368.                 InitRuleBook;
  369.             ge_FinishRuleBook: 
  370.                 FinishRuleBook;
  371.             otherwise begin
  372.                 if gameevent.event = ge_NewGame then begin
  373.                     SetHandleSize(gameevent.game, sizeof(gameRecord));
  374.                 end; (* if *)
  375.                 s1 := HGetState(gameevent.game);
  376.                 HLock(gameevent.game);
  377.                 s2 := HGetState(gameevent.globals);
  378.                 HLock(gameevent.globals);
  379.                 PseudoMain(gamePeek(gameevent.game)^^, globalsPeek(gameevent.globals)^^);
  380.                 HSetState(gameevent.game, s1);
  381.                 HSetState(gameevent.globals, s2);
  382.             end; (* otherwise *)
  383.         end; (* case *)
  384.     end; { Main }
  385.  
  386. end. { TicTacToe }